import cv2
import numpy as np
import os
import json
from matplotlib import pyplot as plt
from shapely import Point
from order_polygon_ccw import order_polygon_ccw

def export_contours_to_json(contours, filename):
    """
    将轮廓点导出为JSON格式
    参数:
    contours: 轮廓列表
    filename: 输出文件名
    """
    contours_data = []
    for i, contour in enumerate(contours):
        points = contour.reshape(-1, 2).tolist()
        # 计算边界矩形
        x, y, w, h = cv2.boundingRect(contour)
        contour_data = {
            "index": i + 1,
            "point_count": len(points),
            "bounding_box": {
                "x": x,
                "y": y,
                "width": w,
                "height": h
            },
            "points": points
        }
        contours_data.append(contour_data)
    with open(filename, 'w') as f:
        json.dump(contours_data, f, indent=2)
    print(f"轮廓点已导出为JSON格式到 {filename}")


def extract_polygon_region(image_path, vertices):
    """
    从图像中截取多边形区域，外部填充为透明（alpha通道）
    参数:
    image_path: 图像文件路径
    vertices: 多边形顶点坐标列表，格式为[(x1,y1), (x2,y2), ...]
    返回:
    cropped_image: 截取的多边形区域图像（带透明通道）
    x, y, w, h: 边界矩形的坐标和尺寸
    """
    # 读取图像
    image = cv2.imread(image_path)
    if image is None:
        raise ValueError("无法读取图像，请检查文件路径")
    # 转换为RGBA格式（添加透明通道）
    image_rgba = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA)
    # 创建透明掩膜
    mask = np.zeros(image.shape[:2], dtype=np.uint8)
    # 将顶点转换为NumPy数组格式
    pts = np.array(vertices, dtype=np.int32)
    # 创建多边形掩膜
    cv2.fillPoly(mask, [pts], 255)
    # 应用掩膜：将多边形区域设置为不透明，外部设置为透明
    image_rgba[:, :, 3] = mask
    # 获取多边形的边界矩形
    x, y, w, h = cv2.boundingRect(pts)
    # 截取矩形区域
    cropped_image = image_rgba[y:y + h, x:x + w]
    return cropped_image, x, y, w, h


def paste_to_new_background(foreground, bg_path, position, bg_size=None):
    """
    将前景图像贴到新背景中
    参数:
    foreground: 前景图像（带透明通道）
    bg_path: 背景图像路径或颜色值（如(255,255,255)表示白色背景）
    position: 前景图像在背景中的位置(x, y)
    bg_size: 背景图像尺寸(width, height)，如果为None则根据前景和位置自动计算
    返回:
    result: 合成后的图像
    """
    # 创建背景图像
    if isinstance(bg_path, str):
        # 从文件读取背景
        background = cv2.imread(bg_path)
        if background is None:
            raise ValueError("无法读取背景图像，请检查文件路径")
        background = cv2.cvtColor(background, cv2.COLOR_BGR2RGBA)
    else:
        # 创建纯色背景
        if bg_size is None:
            # 计算所需背景大小
            bg_height = position[1] + foreground.shape[0]
            bg_width = position[0] + foreground.shape[1]
            bg_size = (bg_width, bg_height)
        # 创建纯色背景
        background = np.zeros((bg_size[1], bg_size[0], 4), dtype=np.uint8)
        background[:, :, :3] = bg_path  # 设置RGB颜色
        background[:, :, 3] = 255  # 设置完全不透明
    # 确保背景足够大以容纳前景图像
    bg_height, bg_width = background.shape[:2]
    fg_height, fg_width = foreground.shape[:2]
    pos_x, pos_y = position
    # 检查位置是否超出背景边界
    if pos_x + fg_width > bg_width or pos_y + fg_height > bg_height:
        # 需要扩展背景
        new_width = max(bg_width, pos_x + fg_width)
        new_height = max(bg_height, pos_y + fg_height)
        # 创建新背景
        new_bg = np.zeros((new_height, new_width, 4), dtype=np.uint8)
        # 如果是纯色背景，填充颜色
        if isinstance(bg_path, tuple):
            new_bg[:, :, :3] = bg_path
            new_bg[:, :, 3] = 255
        else:
            # 如果是图像背景，将原背景复制到新背景中
            new_bg[:bg_height, :bg_width] = background
        background = new_bg
    # 提取前景图像的alpha通道作为掩膜
    alpha_foreground = foreground[:, :, 3] / 255.0
    alpha_background = 1.0 - alpha_foreground
    # 将前景图像合成到背景中
    for c in range(0, 3):
        background[pos_y:pos_y+fg_height, pos_x:pos_x+fg_width, c] = (alpha_foreground*foreground[:,:,c]+alpha_background*background[pos_y:pos_y+fg_height,pos_x:pos_x+fg_width,c]
        )
    return background


def detect_white_regions(image):
    """
    检测图像中的白色区域并返回其边界
    参数:
    image: 输入图像（RGB或RGBA格式）
    返回:
    white_contours: 白色区域的轮廓列表
    white_mask: 白色区域的二值掩膜
    """
    # 如果图像有透明通道，移除它
    if image.shape[2] == 4:
        image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
    # 将图像转换为HSV颜色空间，更容易分离颜色
    hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
    # 定义白色的HSV范围
    # 在HSV中，白色是低饱和度和高亮度
    lower_white = np.array([0, 0, 200])  # 低阈值
    upper_white = np.array([180, 30, 255])  # 高阈值
    # 创建白色区域的掩膜
    white_mask = cv2.inRange(hsv, lower_white, upper_white)
    # 可选：应用形态学操作来改善掩膜
    #kernel = np.ones((5, 5), np.uint8)
    #white_mask = cv2.morphologyEx(white_mask, cv2.MORPH_CLOSE, kernel)
    #white_mask = cv2.morphologyEx(white_mask, cv2.MORPH_OPEN, kernel)
    # 查找轮廓
    contours, _ = cv2.findContours(white_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 过滤掉太小的轮廓（可能是噪声）
    min_area = 0  # 最小面积阈值
    white_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area]
    return white_contours, white_mask


def fill_contours(image, contours, is_red_filled=True):
    """
    将图像中的指定轮廓区域填充为红色
    参数:
    image: 输入图像
    contours: 要填充的轮廓列表
    返回:
    filled_image: 填充后的图像
    """
    # 创建图像的副本
    filled_image = image.copy()
    # 如果图像是RGBA格式，转换为RGB格式以便处理
    if filled_image.shape[2] == 4:
        filled_image = cv2.cvtColor(filled_image, cv2.COLOR_RGBA2RGB)
    # 创建一个与图像大小相同的掩膜
    mask = np.zeros_like(filled_image[:, :, 0])
    # 在掩膜上绘制填充的轮廓
    cv2.drawContours(mask, contours, -1, 255, thickness=cv2.FILLED)
    if is_red_filled:
        # 将掩膜区域填充为红色
        filled_image[mask == 255] = [255, 0, 0]  # BGR格式的红色
    else:
        # 将掩膜区域填充为黑色
        filled_image[mask == 255] = [0, 0, 0]  # BGR格式的黑色
    return filled_image


def calculate_polygon_area(vertices):
    """
    使用Shoelace公式计算多边形面积
    参数:
    vertices: 多边形顶点坐标列表，格式为[(x1,y1), (x2,y2), ...]或numpy数组
    返回:
    area: 多边形的面积
    """
    # 确保顶点是numpy数组格式
    if not isinstance(vertices, np.ndarray):
        vertices = np.array(vertices)
    # 确保多边形是闭合的（第一个点和最后一个点相同）
    if not np.array_equal(vertices[0], vertices[-1]):
        # 如果不是闭合的，添加第一个点作为最后一个点
        vertices = np.vstack([vertices, vertices[0]])
    # 使用Shoelace公式计算面积
    x = vertices[:, 0]
    y = vertices[:, 1]
    # Shoelace公式: area = 1/2 * |Σ(x_i*y_{i+1} - x_{i+1}*y_i)|
    area = 0.5 * np.abs(np.dot(x[:-1], y[1:]) - np.dot(x[1:], y[:-1]))
    return area


# 示例使用
if __name__ == "__main__":
    input_image_path = "missed_areas.png"
    output_image_path = "output_image.png"
    # 定义多边形的四个顶点坐标
    # 2025年9月5日大全全覆盖实验
    field_boundary_coords = [
        [119.7346358, 32.3008244],
        [119.7342016, 32.3006534],
        [119.7347161, 32.2998128],
        [119.7351016, 32.3000005]
    ]
    # 起点
    start_point = Point(119.7346358, 32.3008244)
    ordered_field_boundary_coords = order_polygon_ccw(field_boundary_coords, start_point)

    min_lon_in_field_boundary_coords = np.min([coord[0] for coord in field_boundary_coords])
    max_lon_in_field_boundary_coords = np.max([coord[0] for coord in field_boundary_coords])
    min_lat_in_field_boundary_coords = np.min([coord[1] for coord in field_boundary_coords])
    max_lat_in_field_boundary_coords = np.max([coord[1] for coord in field_boundary_coords])

    lon_min = min_lon_in_field_boundary_coords - 0.00005
    lon_max = max_lon_in_field_boundary_coords + 0.00005
    lat_min = min_lat_in_field_boundary_coords - 0.00005
    lat_max = max_lat_in_field_boundary_coords + 0.00005

    if not os.path.exists(input_image_path):
        print(f"错误: 图像文件 '{input_image_path}' 不存在")

    img = cv2.imread(input_image_path)
    if img is None:
        print(f"错误: 无法读取图像 '{input_image_path}'，请确认文件格式是否正确")
    image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换为RGB格式

    height = img.shape[0]
    width = img.shape[1]

    ordered_field_boundary_coords_local = []
    for i in range(len(ordered_field_boundary_coords)):
        a = width * ((ordered_field_boundary_coords[i][0] - lon_min) / (lon_max - lon_min))
        b = height - height * ((ordered_field_boundary_coords[i][1] - lat_min) / (lat_max - lat_min))
        ordered_field_boundary_coords_local.append([a, b])
    # 计算地块的面积
    field_area = calculate_polygon_area(ordered_field_boundary_coords_local)

    vertices = ordered_field_boundary_coords_local
    # 截取多边形区域（带透明通道）
    cropped_image, x, y, w, h = extract_polygon_region(input_image_path, vertices)
    pos_x = (width-w)//2
    pos_y = (height-h)//2
    # 贴到纯色背景
    white_bg = (0, 0, 255)  # 白色背景
    result_img = paste_to_new_background(cropped_image, white_bg, (pos_x, pos_y), (width, height))
    # 保存结果图像
    cv2.imwrite(output_image_path, cv2.cvtColor(result_img, cv2.COLOR_RGBA2BGRA))

    plt.imshow(result_img)
    plt.axis('off')
    plt.tight_layout()
    plt.show()


    # 假设result_img是从之前代码得到的图像, 检测白色区域
    white_contours, white_mask = detect_white_regions(result_img)
    # 对白色区域进行填充
    red_filled_image = fill_contours(result_img, white_contours)
    # 将白色区域的边界点导出到json文件中
    export_contours_to_json(white_contours, "contours.json")
    # 显示结果
    plt.imshow(red_filled_image)
    plt.axis('off')
    plt.tight_layout()
    plt.show()



    # 输出检测到的白色区域信息
    total_missed_area = 0
    print(f"检测到 {len(white_contours)} 个白色区域")
    for i, cnt in enumerate(white_contours):
        area = cv2.contourArea(cnt)
        total_missed_area = total_missed_area + area
        print(f"区域 {i + 1}: 面积 = {area:.2f} 像素")
        # 获取边界矩形
        x, y, w, h = cv2.boundingRect(cnt)
        print(f"     边界矩形: x={x}, y={y}, 宽度={w}, 高度={h}")

        # 计算矩形右下角坐标
        x2 = x + w
        y2 = y + h
        # 绘制矩形
        cv2.rectangle(red_filled_image, (x, y), (x2, y2), color=(255, 255, 255), thickness=3)


    # 显示结果
    plt.imshow(red_filled_image)
    plt.axis('off')
    plt.tight_layout()
    plt.show()

    missed_rate = total_missed_area/field_area

    print(f"地块面积为: {field_area} 平方单位")
    print(f"遗漏地块面积为: {total_missed_area} 平方单位")
    print(f"遗漏率为: {missed_rate}")